Skip to content

不用 promise, 那些可实现 promise generator

js
function* fun(x) {
  let y = yield x + 1;
  return y;
}
js
function getCallSettings() {
  // utils.ajax方法支持返回promise对象,把得到的promise return出去
  return utils.ajax({
    url: "/dialer/dialerSetting",
    method: "GET",
  });
}
function* dealData() {
  try {
    let settingInfo = yield getCallSettings();
    // do something……
  } catch (err) {
    console.log(err); // 接收错误
  }
}

let it = dealData();
let promise = it.next().value; // 注意,这里拿到yield出来的promise
promise.then(
  (info) => {
    it.next(info); // 拿到info传给yield表达式
  },
  (err) => {
    it.throw(err); // 抛出错误
  }
);
js
function getCallSettings() {
  return fetch('/')
}
function* dealData() {
  try {
    let settingInfo = yield getCallSettings();
    console.log(settingInfo, 'mty');
    // do something……
    return settingInfo
  } catch (err) {
    console.log(err); // 接收错误
  }
  return 'abc';
}

let it = dealData();
let promise = it.next().value; // 注意,这里拿到yield出来的promise
promise.then(
  (info) => {
    it.next(info); // 拿到info传给yield表达式
  },
  (err) => {
    it.throw(err); // 抛出错误
  }
);

Generator 函数和普通函数有什么区别?

  • generator 迭代器函数,可以暂停可以恢复,generator 是协程 在 ES6 实现,最大特点交出函数执行权,控制迭代器的函数,可以暂停可以恢复
  • 不可以用箭头函数声明 -不能当构造函数使用

形式区别

  • function 函数声明有个*
  • 函数内部有 yield,定义不同状态,yield 只能在 generator 里
  • 直接调用 generator 并不会立刻执行,而是返回一个迭代器对象 [iterator Object]
  • 依次调用对象 next() 方法来执行,获取内部状态

生成器函数不能当构造器使用

function* f() {} var obj = new f; // throws "TypeError: f is not a constructor"

交出执行权

如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。

js
function* anotherGenerator(i) {
  yield i + 1;
  yield i + 2;
  yield i + 3;
}

function* generator(i) {
  yield i;
  yield* anotherGenerator(i); // 移交执行权
  yield i + 10;
}

var gen = generator(10);

console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20

传递参数

如果传入了参数,那么这个参数会传给上一条执行的 yield 语句左边的变量

js
function* createIterator() {
  let first = yield 1;
  let second = yield first + 2; // 4 + 2
  // first =4 是next(4)将参数赋给上一条的
  yield second + 3; // 5 + 3
}

let iterator = createIterator();

console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.next(5)); // "{ value: 8, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

使用迭代器遍历二维数组并转换成一维数组:

js
function* iterArr(arr) {
  //迭代器返回一个迭代器对象
  if (Array.isArray(arr)) {
    // 内节点
    for (let i = 0; i < arr.length; i++) {
      yield* iterArr(arr[i]); // (*)递归
    }
  } else {
    // 离开
    yield arr;
  }
}
// 使用 for-of 遍历:
var arr = ["a", ["b", "c"], ["d", "e"]];
for (var x of iterArr(arr)) {
  console.log(x); // a  b  c  d  e
}

// 或者直接将迭代器展开:
var arr = ["a", ["b", ["c", ["d", "e"]]]];
var gen = iterArr(arr);
arr = [...gen]; // ["a", "b", "c", "d", "e"]

Generator 原理

js
// 模拟实现
function* foo() {
  yield "result1";
  yield "result2";
  yield "result3";
}

const gen = foo();
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);

// 生成器函数根据 yield 语句将代码分割为 switch-case 块,后续通过切换_context.prev和_context.next来分别执行各个case
function gen$(_context) {
  while (1) {
    switch ((_context.prev = _context.next)) {
      case 0:
        _context.next = 2;
        return "result1";

      case 2:
        _context.next = 4;
        return "result2";

      case 4:
        _context.next = 6;
        return "result3";

      case 6:
      case "end":
        return _context.stop();
    }
  }
}

var context = {
  next: 0,
  prev: 0,
  done: false,
  stop: function stop() {
    this.done = true;
  },
};

let gen = function () {
  return {
    next: function () {
      value = context.done ? undefined : gen$(context);
      done = context.done;
      return {
        value,
        done,
      };
    },
  };
};

// 测试使用
var g = gen();
g.next(); // {value: "result1", done: false}
g.next(); // {value: "result2", done: false}
g.next(); // {value: "result3", done: false}
g.next(); // {value: undefined, done: true}

async 是 Generator 函数的语法糖,能解释一下吗

async + await => co + generator 语法糖

co

js
const fetchData = (data) => {
  return new Promise((resolve, reject) => {
    setTimeout(() =>{
      resolve(data)
    })
  })
}

function* foo() {
  let data1 = yield fetchData(1)
  data1++;
  let data2 = yield fetchData(data1)
  data2++;
  const data3 = yield fetchData(data2)
  return data3;
}

function co(it) {
  return new Promise(function (resolve, reject) {
    function step(d) {
        let { value, done } = it.next(d);
        if (!done) {
            value.then(function (data) { // 2,txt
              console.log(data);
              step(data)
            }, reject)
        } else {
            resolve(value);
        }
    }
    step();
  });
}

co(foo()).then(function (data) {
  console.log(data)
})

在 MIT 许可下发布